#include <dateopt.h>
#include <rtcopt.h>
#include <fileOpt.h>
#define DATE_TIME_PACK_WAY 1

#define RTC_DEV_FILE_DEFAULT "/dev/rtc0"
#define ZONE_SAVE_FILE_DEFAULT "/tmp/tmZoneDef.txt"
typedef struct
{
    uint64_t tmStamp;
    uint64_t tmSys;
    pthread_mutex_t tmLock;
    int8_t tmUtc;
    char rtcfile[64];
    char zonefile[128];
} tmSdDealStc; // time stamp and date deal struct

static tmSdDealStc tmStmpDateVal;

int tmSdInit(const char *setRtcFile, const char *setZonefile)
{
    if (!(setRtcFile && setRtcFile[0]))
    {
        printf("rtc file setRtcFile null, use defualt:%s!\n", RTC_DEV_FILE_DEFAULT);
        strncpy(tmStmpDateVal.rtcfile, RTC_DEV_FILE_DEFAULT, sizeof(tmStmpDateVal.rtcfile));
    }
    else
    {
        strncpy(tmStmpDateVal.rtcfile, setRtcFile, sizeof(tmStmpDateVal.rtcfile));
    }
    if (!(setZonefile && setZonefile[0]))
    {
        printf("rtc file setZonefile null, use defualt:%s!\n", ZONE_SAVE_FILE_DEFAULT);
        strncpy(tmStmpDateVal.zonefile, ZONE_SAVE_FILE_DEFAULT, sizeof(tmStmpDateVal.zonefile));
    }
    else
    {
        strncpy(tmStmpDateVal.zonefile, setZonefile, sizeof(tmStmpDateVal.zonefile));
    }
    tmStmpDateVal.tmStamp = 0;
    tmStmpDateVal.tmSys = 0;
    pthread_mutex_init(&tmStmpDateVal.tmLock, NULL);

    pthread_mutex_lock(&tmStmpDateVal.tmLock);
    // read zone
    {
        char tmpcmd[256];
        snprintf(tmpcmd, sizeof(tmpcmd), "chmod 777 %s", tmStmpDateVal.zonefile);
        system(tmpcmd);
    }
    char tmpstr[32] = {0};
    if (foptRead(tmStmpDateVal.zonefile, tmpstr, sizeof(tmpstr)) <= 0)
    {
        printf("read zone fail, use defaut:8\n");
        tmStmpDateVal.tmUtc = 8;
        tmpstr[0] = '8';
        foptWrite(tmStmpDateVal.zonefile, tmpstr, 1);
    }
    else
    {
        tmStmpDateVal.tmUtc = strtol(tmpstr, NULL, 10);
        printf("read local zone ok:%d\n", tmStmpDateVal.tmUtc);
    }

    // read rtc date
    tmdateType tmpdate = {0};
    if (rtcDevTmRead(&tmpdate, tmStmpDateVal.rtcfile) < 0)
    {
        printf("read rtc file error\n");
        tmStmpDateVal.tmStamp = 0;
    }
    else
    {
        tmStmpDateVal.tmStamp = date2stamp(&tmpdate);
    }
    struct timespec tmpsysTm;
    clock_gettime(CLOCK_MONOTONIC, &tmpsysTm);
    tmStmpDateVal.tmSys = tmpsysTm.tv_sec;
    pthread_mutex_unlock(&tmStmpDateVal.tmLock);

    return 0;
}

int tmSdSetUtc(int8_t setUtc)
{
    char tmpstr[16] = {0};
    if (!(setUtc >= -11 && setUtc <= 11))
    {
        printf("set utc error:%d\n", setUtc);
        return -1;
    }
    pthread_mutex_lock(&tmStmpDateVal.tmLock);
    tmStmpDateVal.tmUtc = setUtc;
    pthread_mutex_unlock(&tmStmpDateVal.tmLock);
    snprintf(tmpstr, sizeof(tmpstr), "%d", tmStmpDateVal.tmUtc);
    return foptWrite(tmStmpDateVal.zonefile, tmpstr, strlen(tmpstr));
}

int8_t tmSdGetUtc()
{
    return tmStmpDateVal.tmUtc;
}

int tmSdSetStamp(uint64_t setStampSec)
{
    tmdateType tmpdate = stamp2date(setStampSec, 0);
    rtcDevTmWrite(&tmpdate, tmStmpDateVal.rtcfile);

    pthread_mutex_lock(&tmStmpDateVal.tmLock);
    tmStmpDateVal.tmStamp = setStampSec;
    struct timespec tmpsysTm;
    clock_gettime(CLOCK_MONOTONIC, &tmpsysTm);
    tmStmpDateVal.tmSys = tmpsysTm.tv_sec;
    pthread_mutex_unlock(&tmStmpDateVal.tmLock);

    // sync time to systime
    char tmpstr[128];
    snprintf(tmpstr, sizeof(tmpstr), "hwclock -s -f %s", tmStmpDateVal.rtcfile);
    system(tmpstr);
}

int tmSdSetDate(tmdateType *setTmDate)
{
    // get stamp, delete time zone.
    tmSdSetStamp(date2stamp(setTmDate));
    return 1;
}

uint64_t tmSdGetStampSec()
{
    uint64_t tmpstamp;
    struct timespec tmpSysTm;
    clock_gettime(CLOCK_MONOTONIC, &tmpSysTm);
    pthread_mutex_lock(&tmStmpDateVal.tmLock);
    tmpstamp = tmStmpDateVal.tmStamp + tmpSysTm.tv_sec - tmStmpDateVal.tmSys;
    pthread_mutex_unlock(&tmStmpDateVal.tmLock);
    return tmpstamp;
}
uint64_t tmSdGetStampMs()
{
    uint64_t tmpstamp;
    struct timespec tmpSysTm;
    clock_gettime(CLOCK_MONOTONIC, &tmpSysTm);
    pthread_mutex_lock(&tmStmpDateVal.tmLock);
    tmpstamp = (tmStmpDateVal.tmStamp + tmpSysTm.tv_sec - tmStmpDateVal.tmSys) * 1000 + (tmpSysTm.tv_nsec / 1000000);
    pthread_mutex_unlock(&tmStmpDateVal.tmLock);
    return tmpstamp;
}
uint64_t tmSdGetStampUs()
{
    uint64_t tmpstamp;
    struct timespec tmpSysTm;
    clock_gettime(CLOCK_MONOTONIC, &tmpSysTm);
    pthread_mutex_lock(&tmStmpDateVal.tmLock);
    tmpstamp = (tmStmpDateVal.tmStamp + tmpSysTm.tv_sec - tmStmpDateVal.tmSys) * 1000000 + (tmpSysTm.tv_nsec / 1000);
    pthread_mutex_unlock(&tmStmpDateVal.tmLock);
    return tmpstamp;
}
uint64_t tmSdGetStampNs()
{
    uint64_t tmpstamp;
    struct timespec tmpSysTm;
    clock_gettime(CLOCK_MONOTONIC, &tmpSysTm);
    pthread_mutex_lock(&tmStmpDateVal.tmLock);
    tmpstamp = (tmStmpDateVal.tmStamp + tmpSysTm.tv_sec - tmStmpDateVal.tmSys) * 1000000000 + (tmpSysTm.tv_nsec);
    pthread_mutex_unlock(&tmStmpDateVal.tmLock);
    return tmpstamp;
}

tmdateType tmSdGetDate()
{
    tmdateType tmpdt;
    uint64_t tmpstmp = tmSdGetStampSec();
    tmpdt = stamp2date(tmpstmp, tmStmpDateVal.tmUtc);
    struct timespec tmpsysTm;
    clock_gettime(CLOCK_MONOTONIC, &tmpsysTm);
    tmpdt.nanosec = tmpsysTm.tv_nsec;
    return tmpdt;
}
